const crypto = require('crypto')
const IoRedis = require('siwi-ioredis')
const constant = require('../constant')
const Cache = require('siwi-cache')
const request = require('../modules/request')
const Utils = require('../utils/utils')
const utils = new Utils()
class Wechat {
    constructor(options) {
        this.options = options
        this.appid = options.appid
        this.appsecret = options.appsecret
        this.token = options.token
        this.aeskey = options.aeskey
        this.mch_id = options.mch_id
        this.cache = options.cache
        this.cache_drive = options.cache.drive
        this.redis_options = options.cache.redis
        this.file_options = options.cache.file
        this.access_token = null
    }
    async checkSignature(signature, timestamp, nonce, echostr) {
        const str = [this.Token, timestamp, nonce].sort().join('')
        const sha1 = crypto.createHash('sha1')
        const sign = sha1.update(str).digest('hex')
        if (sign == signature) {
            return echostr
        }
        return false
    }

    /**
     * 获取 access_token
     * @param {String} pre_access_token 外界传入的 access_token
     */
    async getAccessToken(pre_access_token = false) {
        /* 优先使用传入的token */
        if (pre_access_token) {
            return pre_access_token
        }

        /* redis */
        if (this.redis_options && this.cache_drive == 'redis') {
            const RedisHandler = new IoRedis(this.redis_options)
            const redis = RedisHandler.redisClient
            const access_token = await redis.get(`${constant.SIWI_WECHAT_ACCESS_TOKEN}:APP_ID:${this.appid}`)
            if (access_token) {
                return access_token
            }

            /* 请求微信接口 */
            const uri = `${constant.API_URL_PREFIX}${constant.AUTH_URL}`
            const data = {
                grant_type: 'client_credential',
                appid: this.appid,
                secret: this.appsecret
            }
            const res = await request.get(uri, data)

            /* 记录错误 */
            if (res['errcode']) {
                await redis.setex(`${constant.SIWI_WECHAT_ERROR}:APP_ID:${this.appid}`, 86400 , JSON.stringify(res))
                return false
            }

            /* 缓存 access_token */
            const expires_in = res['expires_in'] - 100;
            await redis.setex(`${constant.SIWI_WECHAT_ACCESS_TOKEN}:APP_ID:${this.appid}`, expires_in ,res['access_token'])
            this.access_token = res['access_token']
            return res['access_token']
        } 

        /* 文件缓存 不推荐 */
        const cache = new Cache(this.file_options)
        const access_token = await cache.get(`${constant.SIWI_WECHAT_ACCESS_TOKEN}:APP_ID:${this.appid}`)
        if (access_token) {
            return access_token
        }

        /* 请求微信接口 */
        const uri = `${constant.API_URL_PREFIX}${constant.AUTH_URL}`
        const data = {
            grant_type: 'client_credential',
            appid: this.appid,
            secret: this.appsecret
        }
        const res = await request.get(uri, data)

        /* 记录错误 */
        if (res['errcode']) {
            await cache.set(`${constant.SIWI_WECHAT_ERROR}:APP_ID:${this.appid}`, JSON.stringify(res))
            return false
        }

        /* 缓存 access_token */
        const expires_in = res['expires_in'] - 100;
        await cache.set(`${constant.SIWI_WECHAT_ACCESS_TOKEN}:APP_ID:${this.appid}`, res['access_token'], expires_in)
        this.access_token = res['access_token']
        return res['access_token']
    }

    /**
     * mixin 集合 目标类的方法到wechat
     * @param {*} source 
     */
    mixin(source) {
        for (const key of Reflect.ownKeys(source.prototype)) {
            if (key !== 'length' && key != 'construct' && key !== 'name') {
                Object.defineProperty(Wechat.prototype, key, Object.getOwnPropertyDescriptor(source.prototype, key))
            }
        }
    }

    /**
     * 接收微信xml消息推送
     * @param {*} xmlMsg 
     */
    async receiveMessages(xmlMsg) {
        if (!xmlMsg) {
            return false
        }
        const res = await utils.xmlToJson(xmlMsg)
        if (!res) {
            return false
        }
        return res['xml']
    }
    /**
     * 随机字符串
     * @param {*} length 
     */
    async generateNonceStr(length = 16) {
        const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
        let str = ""
        for (let i = 0; i < length; i++) {
            str += chars[`${Math.ceil(Math.random() * 61)}`]
        }
        return str
    }

    /**
     * 签名
     * @param {*} data 
     */
    async sign(data) {
        let str = ''
        data['key'] = this.appid
        for (const item of Object.keys(data).sort()) {
            if (data[item]) {
                str += `${item}=${data[item]}&`
            }
        }
        str = str.substring(0, str.lastIndexOf('&'))
        // console.log(str)
        const md5 = crypto.createHash('md5')
        const sign = md5.update(str).digest('hex')
        return sign
    }

}

module.exports = Wechat